using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ICP_MS.Plugin.Tune;

public class MassCalibration
{
	public int sweeps = 10;

	private const int trys = 20;

	private const int region = 10;

	private const double step = 0.05;

	private List<MassFactor> massFactors = new List<MassFactor>();

	private List<MassHexapole> massHexapoles = new List<MassHexapole>();

	private bool isRunCalibration = false;

	private AutoResetEvent scanEvent = new AutoResetEvent(initialState: false);

	private StringBuilder log = new StringBuilder();

	public Resolution CurrentResolution = new Resolution
	{
		ResolutionType = 1,
		LowLimit = 0.6,
		HighLimit = 0.8
	};

	private const double a = 0.237;

	private const double q = 0.706;

	private const double pai = 3.1415926;

	private const double n = 2.0;

	private const double z = 1.0;

	private const double r0 = 0.005325;

	private const double sm = 1.66053873;

	private const double e = 1.0;

	private const double se = 1.602176462;

	private const double coe = 100000000.0;

	private double deltaDC = 0.1;

	private double deltaRF = 1.0;

	private double oldDC = double.NaN;

	private double oldRF = double.NaN;

	public List<MassFactor> MassFactors => massFactors;

	public List<MassHexapole> MassHexapole => massHexapoles;

	public Spectrum[] CurrentSpectums { get; set; }

	public string LogTxt => log.ToString();

	public bool IsRunCalibration => isRunCalibration;

	public event EventHandler RefreshFactors = null;

	public event EventHandler RefreshSpectrums = null;

	public event EventHandler CalibrationFinish = null;

	private void RefreshFactorsEx()
	{
		if (this.RefreshFactors != null)
		{
			this.RefreshFactors(null, null);
		}
	}

	private void RefreshSpectrumsEx()
	{
		if (this.RefreshSpectrums != null)
		{
			this.RefreshSpectrums(null, null);
		}
	}

	private void CalibrationFinishEx()
	{
		if (this.CalibrationFinish != null)
		{
			this.CalibrationFinish(null, null);
		}
	}

	public bool Init()
	{
		IList<MassFactor> factors = DataBase.Instance.GetMSFactors();
		if (factors != null)
		{
			massFactors.AddRange(factors);
		}
		else
		{
			DataBase.Instance.DeleteMSFactors();
		}
		Detector.finishReceive += Detector_massFinishReceive;
		return false;
	}

	public bool Init2()
	{
		IList<MassHexapole> factors2 = DataBase.Instance.GetHexapoleFactors();
		if (factors2 != null)
		{
			massHexapoles.AddRange(factors2);
		}
		else
		{
			DataBase.Instance.DeleteHexapoleFactors();
		}
		return false;
	}

	public void InitForCheck()
	{
		Detector.finishReceive += Detector_massFinishReceive;
	}

	public void DeinitForCheck()
	{
		Detector.finishReceive -= Detector_massFinishReceive;
	}

	private void Detector_errorReceive(object sender, ErrorMsgEventArgs e)
	{
		LensManager.x2lens.Etp = 0.0;
		LensManager.Instance.SetEtp();
		Stop();
		MessageBox.Show("质量数为" + e.X + "元素浓度过高，请检查，已停止测试！", "警告-倍增器保护");
	}

	public bool Dispose()
	{
		Detector.finishReceive -= Detector_massFinishReceive;
		return false;
	}

	public void SelectAll(bool select)
	{
		foreach (MassFactor factor in massFactors)
		{
			factor.run = select;
		}
	}

	public void Save()
	{
		DataBase.Instance.DeleteMSFactors();
		DataBase.Instance.SaveMSFactors(massFactors);
	}

	public void Save2()
	{
		DataBase.Instance.DeleteHexapoleFactors();
		DataBase.Instance.SaveHexapoleFactors(massHexapoles);
	}

	public void Reset()
	{
		scanEvent.Reset();
		foreach (MassFactor factor in massFactors)
		{
			factor.status = 0;
			factor.trys = 0;
		}
	}

	private void ThreadRun()
	{
		oldDC = double.NaN;
		oldRF = double.NaN;
		foreach (MassFactor factor in massFactors)
		{
			if (!factor.run)
			{
				factor.status = 2;
				RefreshFactorsEx();
			}
			else if (!isRunCalibration || !CalibrateFactor(factor))
			{
				break;
			}
		}
		CalibrationFinishEx();
	}

	private void ThreadRunCheck()
	{
		foreach (MassFactor factor in massFactors)
		{
			if (!factor.run)
			{
				factor.status = 2;
				RefreshFactorsEx();
			}
			else if (!isRunCalibration || !CalibrateCheck(factor))
			{
				break;
			}
		}
		CalibrationFinishEx();
	}

	private bool CalibrateCheck(MassFactor factor)
	{
		List<double> mass = SetMassAxis(factor);
		sweeps = 1;
		for (int i = 0; i < sweeps; i++)
		{
			if (!isRunCalibration)
			{
				return true;
			}
			if (i == 0)
			{
				Detector.Instance.SetMassAxis(mass.ToArray());
			}
			List<int> res = new List<int>();
			for (int j = 0; j < mass.Count; j++)
			{
				res.Add(1);
			}
			Detector.Instance.SendHopDatas(mass.ToArray(), res.ToArray(), true, false);
			if (!scanEvent.WaitOne(10000))
			{
				Log.InsertLog("质量数校正-验证", "失败", factor.symbol + "扫描超时(10s)!", (Severity)3);
				MessageBox.Show(factor.symbol + "扫描超时(10s)!");
				return false;
			}
		}
		if (!isRunCalibration)
		{
			return true;
		}
		CalculateCheck(factor);
		RefreshFactorsEx();
		RefreshSpectrumsEx();
		return true;
	}

	private bool CalibrateFactor(MassFactor factor)
	{
		deltaDC = 0.1;
		factor.status = 1;
		RefreshFactorsEx();
		do
		{
			List<double> mass = SetMassAxis(factor);
			List<int> res = new List<int>();
			for (int i = 0; i < mass.Count; i++)
			{
				res.Add(1);
			}
			sweeps = 3;
			for (int j = 0; j < sweeps; j++)
			{
				if (!isRunCalibration)
				{
					return true;
				}
				if (j == 0)
				{
					Detector.Instance.SetMassAxis(mass.ToArray());
				}
				Detector.Instance.SendHopDatas(mass.ToArray(), res.ToArray(), true, true);
				if (!scanEvent.WaitOne(10000))
				{
					Log.InsertLog("质量数校正", "失败", factor.symbol + "扫描超时(10s)!", (Severity)3);
					MessageBox.Show(factor.symbol + "扫描超时(10s)!");
					return false;
				}
			}
			if (!isRunCalibration)
			{
				return true;
			}
			CalculateFactor(factor);
			RefreshFactorsEx();
			RefreshSpectrumsEx();
			switch (factor.status)
			{
			case 3:
				Log.InsertLog("质量数校正", "失败", factor.symbol + "找不到谱峰!", (Severity)3);
				break;
			case 4:
				Log.InsertLog("质量数校正", "失败", factor.symbol + "超出最大校正次数!", (Severity)3);
				break;
			}
		}
		while (isRunCalibration && factor.status == 1);
		return true;
	}

	private bool CalculateFactor(MassFactor factor)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected O, but got Unknown
		Spectrum spectrum = new Spectrum(Detector.msDetectorPara.Mass, Detector.msDetectorPara.IntesnsityPulse.ToArray());
		for (int i = 0; i < spectrum.Intensity.Length; i++)
		{
			spectrum.Intensity[i] /= sweeps;
		}
		double[] baseline = null;
		Spectrum spectrum2 = spectrum.BaseCorrection(1.5, ref baseline);
		if (spectrum2 == null)
		{
			factor.status = 3;
			return false;
		}
		CurrentSpectums = (Spectrum[])(object)new Spectrum[2] { spectrum, spectrum2 };
		RefreshSpectrumsEx();
		SpectrumPeaks peak = spectrum2.FindPeaks(factor.mass, 2.0, 3.0);
		if (peak == null)
		{
			factor.status = 3;
			return false;
		}
		factor.center = peak.mass;
		factor.error = peak.mass - factor.mass;
		factor.width = peak.width;
		factor.trys++;
		if (factor.trys >= 20)
		{
			factor.status = 4;
			return false;
		}
		if (Math.Abs(factor.error) <= 0.1)
		{
			if (factor.width >= CurrentResolution.LowLimit && factor.width <= CurrentResolution.HighLimit)
			{
				factor.status = 2;
			}
			else
			{
				double newDC = ((!(factor.width < CurrentResolution.HighLimit)) ? 1 : ((!(factor.width > CurrentResolution.LowLimit)) ? (-1) : 0));
				if (oldDC != double.NaN && oldDC != newDC)
				{
					deltaDC /= 2.0;
				}
				oldDC = newDC;
				if (factor.width < CurrentResolution.LowLimit)
				{
					factor.kDC -= deltaDC;
				}
				else
				{
					factor.kDC += deltaDC;
				}
			}
		}
		else if (factor.center < factor.mass)
		{
			factor.kRF -= 1.0;
		}
		else
		{
			factor.kRF += 1.0;
		}
		return true;
	}

	public void Start()
	{
		Reset();
		RefreshFactorsEx();
		isRunCalibration = true;
		Detector.Instance.ExperimentState = 3;
		Task.Factory.StartNew(ThreadRun);
	}

	public void StartCheck()
	{
		Reset();
		RefreshFactorsEx();
		isRunCalibration = true;
		Detector.Instance.ExperimentState = 3;
		Task.Factory.StartNew(ThreadRunCheck);
	}

	public void Stop()
	{
		isRunCalibration = false;
		Detector.Instance.ExperimentState = -1;
		scanEvent.Set();
	}

	private void Detector_massFinishReceive(object sender, EventArgs e)
	{
		scanEvent.Set();
	}

	private List<double> SetMassAxis(MassFactor factor)
	{
		double begin = factor.start;
		double end = factor.end;
		List<double> ms = new List<double>();
		for (; begin <= end; begin += 0.05)
		{
			ms.Add(begin);
		}
		Detector.Instance.SetCalibrationFactor(factor.kRF, factor.bRF, factor.kDC, factor.bDC);
		return ms;
	}

	private void SaveSpectrum(Spectrum spectrum)
	{
		string dir = Application.StartupPath + "\\Spectrum";
		try
		{
			if (!Directory.Exists(dir))
			{
				Directory.CreateDirectory(dir);
			}
			string filename = dir + "\\" + DateTime.Now.ToString("yyyy_MM_dd_hh_mm_ss") + ".dat";
			using StreamWriter sw = new StreamWriter(filename);
			int length = spectrum.Intensity.Length;
			for (int i = 0; i < length; i++)
			{
				sw.WriteLine("{0},{1}", spectrum.Mass[i], spectrum.Intensity[i]);
			}
		}
		catch (Exception ex)
		{
			Console.WriteLine("Save spectrum:" + ex.Message);
		}
	}

	private bool CalculateCheck(MassFactor factor)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected O, but got Unknown
		Spectrum spectrum = new Spectrum(Detector.msDetectorPara.Mass, Detector.msDetectorPara.IntesnsityPulse.ToArray());
		for (int i = 0; i < spectrum.Intensity.Length; i++)
		{
			spectrum.Intensity[i] /= sweeps;
		}
		double[] baseline = null;
		Spectrum spectrum2 = spectrum.BaseCorrection(1.5, ref baseline);
		if (spectrum2 == null)
		{
			factor.status = 3;
			return false;
		}
		CurrentSpectums = (Spectrum[])(object)new Spectrum[2] { spectrum, spectrum2 };
		RefreshSpectrumsEx();
		SpectrumPeaks peak = spectrum2.FindPeaks(factor.mass, 2.0, 3.0);
		if (peak == null)
		{
			return false;
		}
		factor.center = peak.mass;
		factor.error = peak.mass - factor.mass;
		factor.width = peak.width;
		return true;
	}

	public bool SaveCsv(string path)
	{
		try
		{
			using StreamWriter sw = new StreamWriter(path);
			sw.WriteLine("{0},{1},{2},{3},{4},{5}", "symbol", "mass", "center", "width", "kDC", "kRF");
			foreach (MassFactor factor in massFactors)
			{
				sw.WriteLine("{0},{1},{2},{3},{4},{5}", factor.symbol, factor.mass, factor.center, factor.width, factor.kDC, factor.kRF);
			}
		}
		catch
		{
		}
		return false;
	}

	public MassFactor GetNearestFactor(double m)
	{
		MassFactor factor = null;
		MassFactor miner = null;
		MassFactor bigger = null;
		for (int i = 0; i < massFactors.Count; i++)
		{
			if (massFactors[i].mass <= m)
			{
				miner = massFactors[i];
			}
			if (massFactors[i].mass >= m)
			{
				bigger = massFactors[i];
				break;
			}
		}
		if (miner == null)
		{
			miner = bigger;
		}
		if (bigger == null)
		{
			bigger = miner;
		}
		factor = new MassFactor();
		if (miner == bigger)
		{
			factor.kDC = RFManager.CalculateDCFromMass(m, (double)QuadMonitor.quad.ReadFreq) + bigger.kDC;
			factor.kRF = RFManager.CalculateRFFromMass(m, (double)QuadMonitor.quad.ReadFreq) + bigger.kRF;
		}
		else
		{
			factor.kDC = ((m - miner.mass) * (RFManager.CalculateDCFromMass(bigger.mass, (double)QuadMonitor.quad.ReadFreq) + bigger.kDC) + (bigger.mass - m) * (RFManager.CalculateDCFromMass(miner.mass, (double)QuadMonitor.quad.ReadFreq) + miner.kDC)) / (bigger.mass - miner.mass);
			factor.kRF = ((m - miner.mass) * (RFManager.CalculateRFFromMass(bigger.mass, (double)QuadMonitor.quad.ReadFreq) + bigger.kRF) + (bigger.mass - m) * (RFManager.CalculateRFFromMass(miner.mass, (double)QuadMonitor.quad.ReadFreq) + miner.kRF)) / (bigger.mass - miner.mass);
		}
		return factor;
	}

	public bool CreateLookupTable(string path)
	{
		try
		{
			string tmpFile = Path.GetTempFileName();
			using (FileStream fs = new FileStream(tmpFile, FileMode.Create, FileAccess.Write))
			{
				using BinaryWriter bw = new BinaryWriter(fs);
				double kdc0 = RFManager.massQP.dck0;
				double bdc0 = RFManager.massQP.dcb0;
				double krf0 = RFManager.massQP.rfk0;
				double brf0 = RFManager.massQP.rfb0;
				double kdc1 = RFManager.massQP.dck1;
				double bdc1 = RFManager.massQP.dcb1;
				double krf1 = RFManager.massQP.rfk1;
				double brf1 = RFManager.massQP.rfb1;
				double i = 0.0;
				double valueRf = 0.0;
				double valueDc = 0.0;
				for (; i <= 260.0; i += 0.01)
				{
					MassFactor factor = GetNearestFactor(i);
					kdc1 = factor.kDC;
					bdc1 = factor.bDC;
					krf1 = factor.kRF;
					brf1 = factor.bRF;
					valueRf = krf0 * factor.kRF * 2.0 + brf0;
					valueDc = kdc0 * factor.kDC + bdc0;
					if (valueRf < 0.0)
					{
						valueRf = 0.0;
					}
					if (valueRf > 65535.0)
					{
						valueRf = 65535.0;
					}
					ushort dacRf = (ushort)valueRf;
					if (valueDc < 0.0)
					{
						valueDc = 0.0;
					}
					if (valueDc > 65535.0)
					{
						valueRf = 65535.0;
					}
					ushort dacDc = (ushort)valueDc;
					bw.Write(dacRf);
					bw.Write(dacDc);
				}
			}
			if (File.Exists(path))
			{
				File.Delete(path);
			}
			File.Copy(tmpFile, path);
			File.Delete(tmpFile);
			return true;
		}
		catch (Exception ex)
		{
			Log.InsertLog("校正", "创建四级杆电源查找表失败!", ex.Message, (Severity)3);
		}
		return false;
	}

	public double CalculateCoeDC(double U, double f)
	{
		double buf = 1.0364268664433792E-08 * (0.237 * (6.2831852 * f) * (6.2831852 * f) * 0.005325 * 0.005325 / 8.0);
		return U / buf;
	}

	public double CalculateCoeRF(double V, double f)
	{
		double buf = 1.0364268664433792E-08 * (0.706 * (6.2831852 * f) * (6.2831852 * f) * 0.005325 * 0.005325 / 4.0);
		return V / buf;
	}

	private double CalculateDCFromMass(double mass, double f)
	{
		return mass * 1.66053873 / 160217646.20000002 * (0.237 * (6.2831852 * f) * (6.2831852 * f) * 0.005325 * 0.005325 / 8.0);
	}

	private double CalculateRFFromMass(double mass, double f)
	{
		return mass * 1.66053873 / 160217646.20000002 * (0.706 * (6.2831852 * f) * (6.2831852 * f) * 0.005325 * 0.005325 / 4.0);
	}

	public bool CreateTable(string path, int order)
	{
		try
		{
			string tmpFile = Application.StartupPath + "\\tmpMass.tmp";
			using (FileStream fs = new FileStream(tmpFile, FileMode.Create, FileAccess.Write))
			{
				using BinaryWriter bw = new BinaryWriter(fs);
				double kdc0 = RFManager.massQP.dck0;
				double bdc0 = RFManager.massQP.dcb0;
				double krf0 = RFManager.massQP.rfk0;
				double brf0 = RFManager.massQP.rfb0;
				List<double> rfList = new List<double>();
				List<double> dcList = new List<double>();
				List<double> massList = new List<double>();
				for (int j = 0; j < massFactors.Count; j++)
				{
					if (MassFactors[j].run)
					{
						rfList.Add((RFManager.CalculateRFFromMass(MassFactors[j].mass, (double)QuadMonitor.quad.ReadFreq) + MassFactors[j].kRF) * 2.0);
						dcList.Add(RFManager.CalculateDCFromMass(MassFactors[j].mass, (double)QuadMonitor.quad.ReadFreq) + MassFactors[j].kDC);
						massList.Add(massFactors[j].mass);
					}
				}
				List<double> weights = new List<double>();
				for (int i = 0; i < massList.Count; i++)
				{
					weights.Add(1.0 / Math.Pow(massList[i], 0.5));
				}
				switch (order)
				{
				case 1:
				{
					double[] kbrf = LSFit.LineFit(massList.ToArray(), rfList.ToArray(), 0.0, 0.0, false);
					double[] kbdc = LSFit.LineFit(massList.ToArray(), dcList.ToArray(), 0.0, 0.0, false);
					if (kbrf == null || kbdc == null)
					{
						break;
					}
					double l = 0.0;
					double valueRf2 = 0.0;
					double valueDc2 = 0.0;
					for (; l <= 260.0; l += 0.01)
					{
						valueRf2 = krf0 * (kbrf[0] * l + kbrf[1]) + brf0;
						valueDc2 = kdc0 * (kbdc[0] * l + kbdc[1]) + bdc0;
						if (valueRf2 < 0.0)
						{
							valueRf2 = 0.0;
						}
						if (valueRf2 > 65535.0)
						{
							valueRf2 = 65535.0;
						}
						ushort dacRf2 = (ushort)valueRf2;
						if (valueDc2 < 0.0)
						{
							valueDc2 = 0.0;
						}
						if (valueDc2 > 65535.0)
						{
							valueRf2 = 65535.0;
						}
						ushort dacDc2 = (ushort)valueDc2;
						bw.Write(dacRf2);
						bw.Write(dacDc2);
					}
					break;
				}
				case 2:
				{
					double[] kbrf2 = LSFit.Polynomial(massList.ToArray(), rfList.ToArray(), 2);
					double[] kbdc2 = LSFit.Polynomial(massList.ToArray(), dcList.ToArray(), 2);
					if (kbrf2 == null || kbdc2 == null)
					{
						break;
					}
					double k = 0.0;
					double valueRf = 0.0;
					double valueDc = 0.0;
					for (; k <= 260.0; k += 0.01)
					{
						valueRf = krf0 * (kbrf2[2] * k * k + kbrf2[1] * k + kbrf2[0]) + brf0;
						valueDc = kdc0 * (kbdc2[2] * k * k + kbdc2[1] * k + kbdc2[0]) + bdc0;
						if (valueRf < 0.0)
						{
							valueRf = 0.0;
						}
						if (valueRf > 65535.0)
						{
							valueRf = 65535.0;
						}
						ushort dacRf = (ushort)valueRf;
						if (valueDc < 0.0)
						{
							valueDc = 0.0;
						}
						if (valueDc > 65535.0)
						{
							valueRf = 65535.0;
						}
						ushort dacDc = (ushort)valueDc;
						bw.Write(dacRf);
						bw.Write(dacDc);
					}
					break;
				}
				}
			}
			Thread.Sleep(100);
			if (File.Exists(path))
			{
				File.Delete(path);
			}
			Thread.Sleep(100);
			File.Copy(tmpFile, path);
			Thread.Sleep(100);
			File.Delete(tmpFile);
			return true;
		}
		catch (Exception ex)
		{
			Log.InsertLog("校正", "创建四级杆电源查找表失败!", ex.Message, (Severity)3);
		}
		return false;
	}

	public bool CreateTableHex(string path)
	{
		try
		{
			if (MassHexapole.Count <= 0)
			{
				Log.InsertLog("校正", "六极杆校正工作曲线不存在!", "", (Severity)3);
				return false;
			}
			Dictionary<double, List<double>> dictionary = new Dictionary<double, List<double>>();
			HashSet<double> set = new HashSet<double>();
			List<double> listmass = new List<double>();
			int i;
			for (i = 0; i < MassHexapole.Count; i++)
			{
				if (!set.Add(MassHexapole[i].hexfactor))
				{
					continue;
				}
				IOrderedEnumerable<MassHexapole> list = from t in MassHexapole
					where t.hexfactor == MassHexapole[i].hexfactor
					orderby t.mass
					select t;
				Console.WriteLine(list.Count());
				foreach (MassHexapole item2 in list)
				{
					listmass.Add(item2.mass);
				}
				dictionary.Add(MassHexapole[i].hexfactor, new List<double>(listmass));
				listmass.Clear();
			}
			string tmpFile = Path.GetTempFileName();
			using (FileStream fs = new FileStream(tmpFile, FileMode.Create, FileAccess.Write))
			{
				using BinaryWriter bw = new BinaryWriter(fs);
				double j = 0.0;
				foreach (KeyValuePair<double, List<double>> item in dictionary)
				{
					List<double> values = item.Value.ToList();
					while (j <= values.Max())
					{
						double factor = item.Key;
						j += 0.01;
						bw.Write(factor);
					}
				}
			}
			if (File.Exists(path))
			{
				File.Delete(path);
			}
			File.Copy(tmpFile, path);
			File.Delete(tmpFile);
			return true;
		}
		catch (Exception ex)
		{
			Log.InsertLog("校正", "创建六极杆系数查找表失败!", ex.Message, (Severity)3);
		}
		return false;
	}
}
